package xin.exec.executor.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import xin.exec.executor.model.TestAsync;
import xin.exec.executor.repository.TestAsyncRepository;

/**
 * <pre>
 * 测试{@link Async}和{@link Transactional} 事务的有效性
 *
 * 1、一个部分方法上使用了{@link Async}的类的内部，普通{@link Transactional}的方法事务有效
 * 2、同时使用{@link Async}和{@link Transactional}的方法事务有效，异步有效
 * 3、在使用了{@link Transactional} 方法内调用使用了{@link Async}的方法事务有效，异步无效
 * 4、在使用了{@link Async} 方法内调用使用了{@link Transactional}的方法异步有效,事务无效。数据会被写入数据库。
 *    如果设置类全局的(readOnly = true)会报错“Connection is read-only. Queries leading to data modification are not allowed”
 * </pre>
 *
 * @author lixin_ma@outlook.com
 * @version 1.0
 * @since 2019/8/12
 */
@Service
//@Transactional(readOnly = true)
@Slf4j
public class TransactionalService {
	
	
	@Autowired
	private TestAsyncRepository repository;
	
	/**
	 * 一个部分方法上使用了{@link Async}的类的内部，普通{@link Transactional}的方法事务有效
	 */
	@Transactional()
	public void normalMethod(){
		log.info(" 【normalMethod】当前线程 :{},当前对象 :{}",Thread.currentThread().getName(),getClass());
		repository.save(TestAsync.builder().name("normalMethod测试事务").build());
		throw new RuntimeException("normalMethod 测试事务");
	}
	
	
	/**
	 * 同时使用{@link Async}和{@link Transactional}的方法事务有效
	 */
	@Async
	@Transactional()
	public void asyncMethod1(){
		log.info(" 【asyncMethod1】当前线程 :{},当前对象 :{}",Thread.currentThread().getName(),getClass());
		repository.save(TestAsync.builder().name("asyncMethod 测试事务1").build());
		throw new RuntimeException("asyncMethod 测试事务1");
	}
	
	/**
	 * 在使用了{@link Transactional} 方法内调用使用了{@link Async}的方法事务有效正常回滚,但未异步执行
	 * 如果是在异步调用inter2的时候发生异常，asyncMethod2的事务不会回滚，这是正常的
	 */
	@Transactional()
	public void asyncMethod2(){
		log.info(" 【asyncMethod2】当前线程 :{},当前对象 :{}",Thread.currentThread().getName(),getClass());
		repository.save(TestAsync.builder().name("asyncMethod 测试事务2").build());
		
		/**
		 * 如果这里正常调用inter2，就会顺序执行，，此时如果inter2发生异常，事务也会起作用。
		 */
		//inter2();
		
		/**
		 * 这里使用AopContext获取代理，inter2的@Async注解会起作用
		 */
		TransactionalService proxy = (TransactionalService) AopContext.currentProxy();
		log.info(" proxy :{}",proxy.getClass());
		proxy.inter2();
		throw new RuntimeException("asyncMethod 测试事务2");
	}
	
	@Async
	protected void inter2(){
		log.info(" 【inter2】当前线程 :{},当前对象 :{}",Thread.currentThread().getName(),getClass());
		
		/**
		 * 如果是在异步调用inter2的时候发生异常，asyncMethod2的事务不会回滚，这是正常的
		 */
		// throw new RuntimeException("asyncMethod 测试事务2");
	}
	
	
	
	
	/**
	 * 在使用了{@link Async} 方法内调用使用了{@link Transactional}的方法异步有效,事务无效，无法回滚。数据会被写入数据库。
	 * 如果设置类全局的(readOnly = true)会报错“Connection is read-only. Queries leading to data modification are not allowed”
	 */
	@Async
	public void asyncMethod3(){
		log.info(" 【asyncMethod3】当前线程 :{},当前对象 :{}",Thread.currentThread().getName(),getClass());
		
		/**
		 * 如果这里正常调用inter3，如果这里正常调用inter3上的事务不会起作用。
		 */
		//inter3();
		
		/**
		 * 这里使用AopContext获取代理，inter3的inter3上的事务会起作用
		 */
		TransactionalService proxy = (TransactionalService) AopContext.currentProxy();
		log.info(" proxy :{}",proxy.getClass());
		proxy.inter3();
	}
	
	@Transactional()
	protected void inter3(){
		log.info(" 【inter3】当前线程 :{},当前对象 :{}",Thread.currentThread().getName(),getClass());
		repository.save(TestAsync.builder().name("asyncMethod 测试事务3").build());
		throw new RuntimeException("asyncMethod 测试事务3");
	}
}
